#include "Inf_SolenoidValve.h"

#define SLOW_RISE_LOWER_SHIFT_TIME_MS 100 /* 缓升缓降切换PWM间隔时间 ms */

#if FreeRTOS
#define __DELAY__() vTaskDelay(SLOW_RISE_LOWER_SHIFT_TIME_MS / portTICK_PERIOD_MS)
#else
#define __DELAY__() FL_DelayMs(SLOW_RISE_LOWER_SHIFT_TIME_MS)
#endif

typedef enum
{
    DUTY_CYCLE_C_VOLTAGE_0V  = 0,
    DUTY_CYCLE_C_VOLTAGE_4V  = 33,
    DUTY_CYCLE_C_VOLTAGE_6V  = 50,
    DUTY_CYCLE_C_VOLTAGE_8V  = 67,
    DUTY_CYCLE_C_VOLTAGE_12V = 95,
} DutyCycleCorrespondVoltage_e;

static void Inf_SolenoidValve_ControlSolenoidValve(const SolenoidValve *solenoidValve);

/* 电磁阀列表 */
SolenoidValve solenoidValveList[] = {
    [SOLENOID_VALVE_INDEX_AM1]               = { .solenoidValveType          = SOLENOID_VALVE_TYPE_AM1,
                                                 .solenoidValveUse           = SOLENOID_VALVE_USE_MASSAGE,
                                                 .dutyCycle                  = 0,
                                                 .isEnable                   = false,
                                                 .timerType                  = TIMER_TYPE_GPTIM,
                                                 .Timer.gptim                = GPTIM1,
                                                 .isComplementary            = false,
                                                 .timerChannel               = FL_GPTIM_CHANNEL_3,
                                                 .port                       = GPIOA,
                                                 .pin                        = FL_GPIO_PIN_4,
                                                 .setChannelDutyAndStateFunc = Driver_GPTIM_SetChannelDutyAndState },
    [SOLENOID_VALVE_INDEX_AM2]               = { .solenoidValveType          = SOLENOID_VALVE_TYPE_AM2,
                                                 .solenoidValveUse           = SOLENOID_VALVE_USE_MASSAGE,
                                                 .dutyCycle                  = 0,
                                                 .isEnable                   = false,
                                                 .timerType                  = TIMER_TYPE_GPTIM,
                                                 .Timer.gptim                = GPTIM1,
                                                 .isComplementary            = false,
                                                 .timerChannel               = FL_GPTIM_CHANNEL_4,
                                                 .port                       = GPIOA,
                                                 .pin                        = FL_GPIO_PIN_5,
                                                 .setChannelDutyAndStateFunc = Driver_GPTIM_SetChannelDutyAndState },
    [SOLENOID_VALVE_INDEX_AM3]               = { .solenoidValveType          = SOLENOID_VALVE_TYPE_AM3,
                                                 .solenoidValveUse           = SOLENOID_VALVE_USE_MASSAGE,
                                                 .dutyCycle                  = 0,
                                                 .isEnable                   = false,
                                                 .timerType                  = TIMER_TYPE_ATIM,
                                                 .Timer.atim                 = ATIM,
                                                 .isComplementary            = false,
                                                 .timerChannel               = FL_ATIM_CHANNEL_4,
                                                 .port                       = GPIOB,
                                                 .pin                        = FL_GPIO_PIN_7,
                                                 .setChannelDutyAndStateFunc = Driver_ATIM_SetChannelDutyAndState },
    [SOLENOID_VALVE_INDEX_AM4]               = { .solenoidValveType          = SOLENOID_VALVE_TYPE_AM4,
                                                 .solenoidValveUse           = SOLENOID_VALVE_USE_MASSAGE,
                                                 .dutyCycle                  = 0,
                                                 .isEnable                   = false,
                                                 .timerType                  = TIMER_TYPE_ATIM,
                                                 .Timer.atim                 = ATIM,
                                                 .isComplementary            = true,
                                                 .timerChannel               = FL_ATIM_CHANNEL_3,
                                                 .port                       = GPIOB,
                                                 .pin                        = FL_GPIO_PIN_8,
                                                 .setChannelDutyAndStateFunc = Driver_ATIM_SetChannelDutyAndState },
    [SOLENOID_VALVE_INDEX_AM5]               = { .solenoidValveType          = SOLENOID_VALVE_TYPE_AM5,
                                                 .solenoidValveUse           = SOLENOID_VALVE_USE_MASSAGE,
                                                 .dutyCycle                  = 0,
                                                 .isEnable                   = false,
                                                 .timerType                  = TIMER_TYPE_GPTIM,
                                                 .Timer.gptim                = GPTIM0,
                                                 .isComplementary            = false,
                                                 .timerChannel               = FL_GPTIM_CHANNEL_1,
                                                 .port                       = GPIOB,
                                                 .pin                        = FL_GPIO_PIN_10,
                                                 .setChannelDutyAndStateFunc = Driver_GPTIM_SetChannelDutyAndState },
    [SOLENOID_VALVE_INDEX_AM6]               = { .solenoidValveType          = SOLENOID_VALVE_TYPE_AM6,
                                                 .solenoidValveUse           = SOLENOID_VALVE_USE_MASSAGE,
                                                 .dutyCycle                  = 0,
                                                 .isEnable                   = false,
                                                 .timerType                  = TIMER_TYPE_GPTIM,
                                                 .Timer.gptim                = GPTIM0,
                                                 .isComplementary            = false,
                                                 .timerChannel               = FL_GPTIM_CHANNEL_2,
                                                 .port                       = GPIOB,
                                                 .pin                        = FL_GPIO_PIN_11,
                                                 .setChannelDutyAndStateFunc = Driver_GPTIM_SetChannelDutyAndState },
    [SOLENOID_VALVE_INDEX_AM7]               = { .solenoidValveType          = SOLENOID_VALVE_TYPE_AM7,
                                                 .solenoidValveUse           = SOLENOID_VALVE_USE_MASSAGE,
                                                 .dutyCycle                  = 0,
                                                 .isEnable                   = false,
                                                 .timerType                  = TIMER_TYPE_GPTIM,
                                                 .Timer.gptim                = GPTIM1,
                                                 .isComplementary            = false,
                                                 .timerChannel               = FL_GPTIM_CHANNEL_1,
                                                 .port                       = GPIOC,
                                                 .pin                        = FL_GPIO_PIN_0,
                                                 .setChannelDutyAndStateFunc = Driver_GPTIM_SetChannelDutyAndState },
    [SOLENOID_VALVE_INDEX_AM8]               = { .solenoidValveType          = SOLENOID_VALVE_TYPE_AM8,
                                                 .solenoidValveUse           = SOLENOID_VALVE_USE_MASSAGE,
                                                 .dutyCycle                  = 0,
                                                 .isEnable                   = false,
                                                 .timerType                  = TIMER_TYPE_GPTIM,
                                                 .Timer.gptim                = GPTIM1,
                                                 .isComplementary            = false,
                                                 .timerChannel               = FL_GPTIM_CHANNEL_2,
                                                 .port                       = GPIOC,
                                                 .pin                        = FL_GPIO_PIN_1,
                                                 .setChannelDutyAndStateFunc = Driver_GPTIM_SetChannelDutyAndState },
    [SOLENOID_VALVE_INDEX_AM9]               = { .solenoidValveType          = SOLENOID_VALVE_TYPE_AM9,
                                                 .solenoidValveUse           = SOLENOID_VALVE_USE_MASSAGE,
                                                 .dutyCycle                  = 0,
                                                 .isEnable                   = false,
                                                 .timerType                  = TIMER_TYPE_LPTIM32,
                                                 .Timer.lptim32              = LPTIM32,
                                                 .isComplementary            = false,
                                                 .timerChannel               = FL_GPTIM_CHANNEL_1,
                                                 .port                       = GPIOA,
                                                 .pin                        = FL_GPIO_PIN_8,
                                                 .setChannelDutyAndStateFunc = Driver_LPTIM32_SetChannelDutyAndState },
    [SOLENOID_VALVE_INDEX_AM10]              = { .solenoidValveType          = SOLENOID_VALVE_TYPE_AM10,
                                                 .solenoidValveUse           = SOLENOID_VALVE_USE_MASSAGE,
                                                 .dutyCycle                  = 0,
                                                 .isEnable                   = false,
                                                 .timerType                  = TIMER_TYPE_LPTIM32,
                                                 .Timer.lptim32              = LPTIM32,
                                                 .isComplementary            = false,
                                                 .timerChannel               = FL_GPTIM_CHANNEL_2,
                                                 .port                       = GPIOA,
                                                 .pin                        = FL_GPIO_PIN_9,
                                                 .setChannelDutyAndStateFunc = Driver_LPTIM32_SetChannelDutyAndState },
    [SOLENOID_VALVE_INDEX_YTR1]              = { .solenoidValveType          = SOLENOID_VALVE_TYPE_YTR1,
                                                 .solenoidValveUse           = SOLENOID_VALVE_USE_LUMBAR_SUPPORT,
                                                 .dutyCycle                  = 0,
                                                 .isEnable                   = false,
                                                 .timerType                  = TIMER_TYPE_ATIM,
                                                 .Timer.atim                 = ATIM,
                                                 .isComplementary            = false,
                                                 .timerChannel               = FL_ATIM_CHANNEL_1,
                                                 .port                       = GPIOB,
                                                 .pin                        = FL_GPIO_PIN_4,
                                                 .setChannelDutyAndStateFunc = Driver_ATIM_SetChannelDutyAndState },
    [SOLENOID_VALVE_INDEX_YTR2]              = { .solenoidValveType          = SOLENOID_VALVE_TYPE_YTR2,
                                                 .solenoidValveUse           = SOLENOID_VALVE_USE_LUMBAR_SUPPORT,
                                                 .dutyCycle                  = 0,
                                                 .isEnable                   = false,
                                                 .timerType                  = TIMER_TYPE_ATIM,
                                                 .Timer.atim                 = ATIM,
                                                 .isComplementary            = true,
                                                 .timerChannel               = FL_ATIM_CHANNEL_2,
                                                 .port                       = GPIOB,
                                                 .pin                        = FL_GPIO_PIN_3,
                                                 .setChannelDutyAndStateFunc = Driver_ATIM_SetChannelDutyAndState },
    [SOLENOID_VALVE_INDEX_YT3]               = { .solenoidValveType          = SOLENOID_VALVE_TYPE_YT3,
                                                 .solenoidValveUse           = SOLENOID_VALVE_USE_LUMBAR_SUPPORT,
                                                 .dutyCycle                  = 0,
                                                 .isEnable                   = false,
                                                 .timerType                  = TIMER_TYPE_NONE,
                                                 .Timer.lptim32              = NULL, // 随便配置，因为没有使用
                                                 .isComplementary            = false,
                                                 .timerChannel               = SHIFT_REG_CHANNEL_YT3,
                                                 .port                       = NULL,
                                                 .pin                        = 0,
                                                 .setChannelDutyAndStateFunc = Inf_74HC595_SetChannelDutyAndState },
    [SOLENOID_VALVE_INDEX_YTR3]              = { .solenoidValveType          = SOLENOID_VALVE_TYPE_YTR3,
                                                 .solenoidValveUse           = SOLENOID_VALVE_USE_LUMBAR_SUPPORT,
                                                 .dutyCycle                  = 0,
                                                 .isEnable                   = false,
                                                 .timerType                  = TIMER_TYPE_NONE,
                                                 .Timer.lptim32              = NULL, // 随便配置，因为没有使用
                                                 .isComplementary            = false,
                                                 .timerChannel               = SHIFT_REG_CHANNEL_YTR3,
                                                 .port                       = NULL,
                                                 .pin                        = 0,
                                                 .setChannelDutyAndStateFunc = Inf_74HC595_SetChannelDutyAndState },
    [SOLENOID_VALVE_INDEX_YT4]               = { .solenoidValveType          = SOLENOID_VALVE_TYPE_YT4,
                                                 .solenoidValveUse           = SOLENOID_VALVE_USE_LUMBAR_SUPPORT,
                                                 .dutyCycle                  = 0,
                                                 .isEnable                   = false,
                                                 .timerType                  = TIMER_TYPE_NONE,
                                                 .Timer.lptim32              = NULL, // 随便配置，因为没有使用
                                                 .isComplementary            = false,
                                                 .timerChannel               = SHIFT_REG_CHANNEL_YT4,
                                                 .port                       = NULL,
                                                 .pin                        = 0,
                                                 .setChannelDutyAndStateFunc = Inf_74HC595_SetChannelDutyAndState },
    [SOLENOID_VALVE_INDEX_YTR4]              = { .solenoidValveType          = SOLENOID_VALVE_TYPE_YTR4,
                                                 .solenoidValveUse           = SOLENOID_VALVE_USE_LUMBAR_SUPPORT,
                                                 .dutyCycle                  = 0,
                                                 .isEnable                   = false,
                                                 .timerType                  = TIMER_TYPE_NONE,
                                                 .Timer.lptim32              = NULL, // 随便配置，因为没有使用
                                                 .isComplementary            = false,
                                                 .timerChannel               = SHIFT_REG_CHANNEL_YTR4,
                                                 .port                       = NULL,
                                                 .pin                        = 0,
                                                 .setChannelDutyAndStateFunc = Inf_74HC595_SetChannelDutyAndState },
    [SOLENOID_VALVE_INDEX_YT5]               = { .solenoidValveType          = SOLENOID_VALVE_TYPE_YT5,
                                                 .solenoidValveUse           = SOLENOID_VALVE_USE_LUMBAR_SUPPORT,
                                                 .dutyCycle                  = 0,
                                                 .isEnable                   = false,
                                                 .timerType                  = TIMER_TYPE_NONE,
                                                 .Timer.lptim32              = NULL, // 随便配置，因为没有使用
                                                 .isComplementary            = false,
                                                 .timerChannel               = SHIFT_REG_CHANNEL_YT5,
                                                 .port                       = NULL,
                                                 .pin                        = 0,
                                                 .setChannelDutyAndStateFunc = Inf_74HC595_SetChannelDutyAndState },
    [SOLENOID_VALVE_INDEX_YTR5]              = { .solenoidValveType          = SOLENOID_VALVE_TYPE_YTR5,
                                                 .solenoidValveUse           = SOLENOID_VALVE_USE_LUMBAR_SUPPORT,
                                                 .dutyCycle                  = 0,
                                                 .isEnable                   = false,
                                                 .timerType                  = TIMER_TYPE_NONE,
                                                 .Timer.lptim32              = NULL, // 随便配置，因为没有使用
                                                 .isComplementary            = false,
                                                 .timerChannel               = SHIFT_REG_CHANNEL_YTR5,
                                                 .port                       = NULL,
                                                 .pin                        = 0,
                                                 .setChannelDutyAndStateFunc = Inf_74HC595_SetChannelDutyAndState },
    [SOLENOID_VALVE_INDEX_PASSIVE_LEFT_YTR6] = { .solenoidValveType = SOLENOID_VALVE_TYPE_PASSIVE_LEFT_YTR6,
                                                 .solenoidValveUse  = SOLENOID_VALVE_USE_FLANK,
                                                 .dutyCycle         = 0,
                                                 .isEnable          = false,
                                                 .timerType         = TIMER_TYPE_NONE,
                                                 .Timer.lptim32     = NULL, // 随便配置，因为没有使用
                                                 .isComplementary   = false,
                                                 .timerChannel      = SHIFT_REG_CHANNEL_YTR6,
                                                 .port              = NULL,
                                                 .pin               = 0,
                                                 .setChannelDutyAndStateFunc =
                                                     Inf_74HC595_SetChannelDutyAndState },
    [SOLENOID_VALVE_INDEX_PASSIVE_RIGHT_YTR7] = { .solenoidValveType =
                                                      SOLENOID_VALVE_TYPE_PASSIVE_RIGHT_YTR7,
                                                  .solenoidValveUse = SOLENOID_VALVE_USE_FLANK,
                                                  .dutyCycle        = 0,
                                                  .isEnable         = false,
                                                  .timerType        = TIMER_TYPE_NONE,
                                                  .Timer.lptim32    = NULL, // 随便配置，因为没有使用
                                                  .isComplementary  = false,
                                                  .timerChannel     = SHIFT_REG_CHANNEL_YTR7,
                                                  .port             = NULL,
                                                  .pin              = 0,
                                                  .setChannelDutyAndStateFunc =
                                                      Inf_74HC595_SetChannelDutyAndState },
};

/**
 * @brief 初始化电磁阀
 *
 */
void Inf_SolenoidValve_Init(void)
{
    /* YT1-2-Rx AM3-4 配置 */
    Driver_ATIM_Init();

    /* AM1、2、5、6、7、8配置 */
    Driver_GPTIM_Init();

    /* AM9、10配置 */
    Driver_LPTIM32_Init();

    /* 初始化YT3-4-5\R, YTR6-7  */
    Inf_74HC595_Init();

    /* 初始化 VALVE_EN GPIO */
    Inf_BTS7020_Init();
}

/**
 * @brief 使能电磁阀开关
 *
 */
void Inf_SolenoidValve_EnableSwitch(void)
{
    Inf_BTS7020_EnableValveEN();
    solenoidValveSwitchIsOn = true;
}

/**
 * @brief 失能电磁阀
 *
 */
void Inf_SolenoidValve_DisableSwitch(void)
{
    Inf_BTS5090_DisableValveEN();
    solenoidValveSwitchIsOn = false;
}

/**
 * @brief 激活指定的电磁阀
 *
 * @param solenoidValve 阀指针
 */
void Inf_SolenoidValve_ActivateSolenoidValve(SolenoidValve *solenoidValve)
{
    solenoidValve->isEnable = true;

    static const uint8_t SLOW_RISE_LOWER_DUTY_CYCLE_LIST[] = {
        DUTY_CYCLE_C_VOLTAGE_4V, DUTY_CYCLE_C_VOLTAGE_8V, DUTY_CYCLE_C_VOLTAGE_12V, DUTY_CYCLE_C_VOLTAGE_8V,
        DUTY_CYCLE_C_VOLTAGE_6V
    };
    static const uint8_t LIST_SIZE =
        sizeof(SLOW_RISE_LOWER_DUTY_CYCLE_LIST) / sizeof(SLOW_RISE_LOWER_DUTY_CYCLE_LIST[0]);

    // 缓升缓降
    for (uint8_t i = 0; i < LIST_SIZE; i++) {
        solenoidValve->dutyCycle = SLOW_RISE_LOWER_DUTY_CYCLE_LIST[i];
        Inf_SolenoidValve_ControlSolenoidValve(solenoidValve);
        __DELAY__();
    }
}

/**
 * @brief 禁用指定的电磁阀
 *
 * @param solenoidValve 阀指针
 */
void Inf_SolenoidValve_DeactivateSolenoidValve(SolenoidValve *solenoidValve)
{
    /* 若一开始就是禁用状态，则直接返回 */
    if (solenoidValve->isEnable == false) {
        return;
    }

    static const uint8_t SLOW_RISE_LOWER_DUTY_CYCLE_LIST[] = { DUTY_CYCLE_C_VOLTAGE_4V,
                                                               DUTY_CYCLE_C_VOLTAGE_0V };
    static const uint8_t LIST_SIZE =
        sizeof(SLOW_RISE_LOWER_DUTY_CYCLE_LIST) / sizeof(SLOW_RISE_LOWER_DUTY_CYCLE_LIST[0]);

    // 缓升缓降
    for (uint8_t i = 0; i < LIST_SIZE; i++) {
        solenoidValve->dutyCycle = SLOW_RISE_LOWER_DUTY_CYCLE_LIST[i];

        /* 如果占空比为0，才让电磁阀使能为false */
        if (solenoidValve->dutyCycle == DUTY_CYCLE_C_VOLTAGE_0V) {
            solenoidValve->isEnable = false;
        }

        Inf_SolenoidValve_ControlSolenoidValve(solenoidValve);
        __DELAY__();
    }
}

/**
 * @brief 同时激活多个电磁阀（避免延时导致的滞后问题）
 *
 * @param valveIndexes 阀门索引数组
 * @param count 阀门数量
 */
void Inf_SolenoidValve_ActivateMultiValves(const uint8_t *valveIndexes, uint8_t count)
{
    if (valveIndexes == NULL || count == 0 || count > SOLENOID_VALVE_INDEX_MAX) {
        return;
    }

    LOG("Inf_SolenoidValve_ActivateMultiValves(): count = %d index1 = %d, index2 = %d\r\n", count,
        valveIndexes[0], valveIndexes[1]);

    // 准备所有阀门
    for (uint8_t i = 0; i < count; i++) {
        uint8_t index = valveIndexes[i];
        if (index < SOLENOID_VALVE_INDEX_MAX) {
            solenoidValveList[index].isEnable = true;
        }
    }

    // 缓升缓降占空比列表
    static const uint8_t SLOW_RISE_LOWER_DUTY_CYCLE_LIST[] = {
        DUTY_CYCLE_C_VOLTAGE_4V, DUTY_CYCLE_C_VOLTAGE_8V, DUTY_CYCLE_C_VOLTAGE_12V, DUTY_CYCLE_C_VOLTAGE_8V,
        DUTY_CYCLE_C_VOLTAGE_6V
    };
    static const uint8_t LIST_SIZE =
        sizeof(SLOW_RISE_LOWER_DUTY_CYCLE_LIST) / sizeof(SLOW_RISE_LOWER_DUTY_CYCLE_LIST[0]);

    // 同步缓升缓降
    for (uint8_t step = 0; step < LIST_SIZE; step++) {
        // 为所有阀门设置相同的占空比
        for (uint8_t i = 0; i < count; i++) {
            uint8_t index = valveIndexes[i];
            if (index < SOLENOID_VALVE_INDEX_MAX) {
                solenoidValveList[index].dutyCycle = SLOW_RISE_LOWER_DUTY_CYCLE_LIST[step];
                Inf_SolenoidValve_ControlSolenoidValve(&solenoidValveList[index]);
            }
        }
        // 每个步骤只延时一次
        __DELAY__();
    }
}

/**
 * @brief 同时禁用多个电磁阀
 *
 * @param valveIndexes 阀门索引数组
 * @param count 阀门数量
 */
void Inf_SolenoidValve_DeactivateMultiValves(const uint8_t *valveIndexes, uint8_t count)
{
    if (valveIndexes == NULL || count == 0 || count > SOLENOID_VALVE_INDEX_MAX) {
        return;
    }

    // 缓降占空比列表
    static const uint8_t SLOW_RISE_LOWER_DUTY_CYCLE_LIST[] = { DUTY_CYCLE_C_VOLTAGE_4V,
                                                               DUTY_CYCLE_C_VOLTAGE_0V };
    static const uint8_t LIST_SIZE =
        sizeof(SLOW_RISE_LOWER_DUTY_CYCLE_LIST) / sizeof(SLOW_RISE_LOWER_DUTY_CYCLE_LIST[0]);

    // 同步缓降
    for (uint8_t step = 0; step < LIST_SIZE; step++) {
        // 为所有阀门设置相同的占空比
        for (uint8_t i = 0; i < count; i++) {
            uint8_t index = valveIndexes[i];

            /* 如果阀门使能为false，则跳过 */
            if (solenoidValveList[index].isEnable == false) {
                continue;
            }

            if (index < SOLENOID_VALVE_INDEX_MAX) {
                solenoidValveList[index].dutyCycle = SLOW_RISE_LOWER_DUTY_CYCLE_LIST[step];

                // 如果占空比为0，设置使能为false
                if (SLOW_RISE_LOWER_DUTY_CYCLE_LIST[step] == DUTY_CYCLE_C_VOLTAGE_0V) {
                    solenoidValveList[index].isEnable = false;
                }

                Inf_SolenoidValve_ControlSolenoidValve(&solenoidValveList[index]);
            }
        }
        // 每个步骤只延时一次
        __DELAY__();
    }
}

/**
 * @brief 控制电磁阀
 *
 * @param solenoidValve 电磁阀指针
 */
static void Inf_SolenoidValve_ControlSolenoidValve(const SolenoidValve *solenoidValve)
{
    /* 准备定时器 */
    void *timer = NULL;
    switch (solenoidValve->timerType) {
    case TIMER_TYPE_ATIM:    timer = solenoidValve->Timer.atim; break;
    case TIMER_TYPE_GPTIM:   timer = solenoidValve->Timer.gptim; break;
    case TIMER_TYPE_LPTIM32: timer = solenoidValve->Timer.lptim32; break;
    }

    /* 控制阀开关 */
    solenoidValve->setChannelDutyAndStateFunc(timer, solenoidValve->timerChannel,
                                              solenoidValve->isComplementary, solenoidValve->dutyCycle,
                                              solenoidValve->isEnable);
}
